﻿using ImageK.Gui;
using Microsoft.VisualBasic.ApplicationServices;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO.Packaging;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ImageK.Plugin.Frame
{
    /** This plugin continuously plots ImageJ's memory utilization. 
        Click on the plot to force the JVM to do garbage collection. */
    public class MemoryMonitor : PlugInFrame
    {

        private static double scale = Prefs.getGuiScale();
        private static int width = (int)(250*scale);
        private static int height = (int)(90*scale);
        private static string LOC_KEY = "memory.loc";
        private static MemoryMonitor instance;
        private static Image image;
        private Graphics g;
        private Pen _pen = new Pen(Color.Black);
        private SolidBrush _brush = new SolidBrush(Color.Black);
        private int frames;
        private double[] mem;
        private int index;
        private long value;
        private double defaultMax = 20*1024*1024; // 20MB
        // private double max = defaultMax;
        private double max = 20*1024*1024; // 20MB
        private long maxMemory = IJ.maxMemory();
        private bool done;
        private PlotCanvas ic;
        private System.ComponentModel.IContainer components;
        private Thread _updateThread;
        public MemoryMonitor()
        {
            Text = "Memory";
            if (instance!=null)
            {
                WindowManager.toFront(instance);
                return;
            }
            instance = this;
            WindowManager.addWindow(this);

            // setLayout(new BorderLayout());
            InitializeComponent();
            
            // setResizable(false);
            // pack();
            // Point loc = Prefs.getLocation(LOC_KEY);
            // if (loc!=null)
            //     setLocation(loc);
            // else
            //     GUI.centerOnImageJScreen(this);
            this.StartPosition = FormStartPosition.CenterParent;
            // image = createImage(width, height);
            image = new Bitmap(width, height);
            g =  Graphics.FromImage(image);
            // g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            _brush.Color = Color.White;
            g.FillRectangle(_brush, 0, 0, width, height);
            this.Font = new Font("SansSerif", (int)(12*Prefs.getGuiScale()), FontStyle.Regular);
            
            ImageK ij = IJ.GetInstance();
            if (ij!=null)
            {
                // addKeyListener(ij);
                // ic.addKeyListener(ij);
                // ic.addMouseListener(ij);
            }
            mem = new double[width+1];
            // Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
            Show();
            _updateThread = new Thread(updateMemory)
            {
                Priority = ThreadPriority.Lowest
            };
            _updateThread.Start();

        }

        private void InitializeComponent()
        {
            components=new System.ComponentModel.Container();
            ic = new PlotCanvas();
            SuspendLayout();
            ic.Width =width;
            ic.Height = height;
            Controls.Add(ic);
            ResumeLayout(false);

            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            this.AutoSize = true;
        }

        private void updateMemory()
        {
            while (!done)
            {
                updatePlot();
                addText();
                if (this.InvokeRequired)
                {
                    try
                    {
                        this.Invoke(ic.Refresh);
                    }
                    catch ( ObjectDisposedException ex)
                    {

                    }
                }
                else
                {
                    ic.Refresh();
                }
                IJ.wait(50);
                frames++;
            }
        }


        void addText()
        {
            double value2 = (double)value/1048576L;
            String s = IJ.d2s(value2, value2>50 ? 0 : 2)+"MB";
            if (maxMemory>0L)
            {
                double percent = value*100/maxMemory;
                s += " ("+(percent<1.0 ? "<1" : IJ.d2s(percent, 0)) + "%)";
            }
            _brush.Color = Color.Black;
            g.DrawString(s, Font, _brush, 2, 15);
            string images = ""+WindowManager.getImageCount();
            g.DrawString(images, Font, _brush, width-(5+images.Length*8), 15);
        }

        void updatePlot()
        {
            double used = IJ.currentMemory();
            if (frames%10==0) value = (long)used;
            if (used>0.86*max) max *= 2.0;
            mem[index++] = used;
            if (index==mem.Length) index = 0;
            double maxmax = 0.0;
            for (int i = 0; i<mem.Length; i++)
            {
                if (mem[i]>maxmax) maxmax= mem[i];
            }
            if (maxmax<defaultMax) max=defaultMax*2;
            if (maxmax<defaultMax/2) max = defaultMax;
            int index2 = index+1;
            if (index2==mem.Length) index2 = 0;
            _brush.Color = Color.White;
            g.FillRectangle(_brush, 0, 0, width, height);
            _pen.Color = Color.Black;
            double scale = height/max;
            int x1 = 0;
            int y1 = height-(int)(mem[index2]*scale);
            for (int x2 = 1; x2<width; x2++)
            {
                index2++;
                if (index2==mem.Length) index2 = 0;
                int y2 = height-(int)(mem[index2]*scale);
                g.DrawLine(_pen, x1, y1, x2, y2);
                x1=x2; y1=y2;
            }
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            instance = null;
            Prefs.saveLocation(LOC_KEY, this.Location);
            done = true;
            base.OnFormClosing(e);
        }

        // public void close()
        // {
        //     Close();
        //     instance = null;
        //     Prefs.saveLocation(LOC_KEY, this.Location);
        //     done = true;
        // }

        class PlotCanvas : Panel
        {
            protected override void OnPaint(PaintEventArgs e)
            {
                var g = e.Graphics;
                g.DrawImage(image, 0, 0);
            }
        }


    }



}
